
package com.hlkj.pay.app.merchant.ext.impl.fuyou.ext;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.bean.BeanUtil;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.constant.FuYouConstant;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.dto.FuYouAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.dto.FuYouConfig;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.dto.FuYouPayOrderResultDto;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.utils.HttpUtils;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.utils.SignUtils;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * 富友 正扫 反扫 通用处理
 * 
 * @author HlpayTeam
 * @date 2024/10/11 16:22
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class FuYouSceneCommonPayOrder {

    /**
     * 富友查询支付订单
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderQueryContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        FuYouConfig fuYouConfig = JsonUtils.parseObject(channelConfig, FuYouConfig.class);
        PayOrderDO payOrderDO = channelPayOrderQueryContext.getPayOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelPayOrderQueryContext.getMerchantAppChannelDO();
        FuYouAppChannelParams fuYouAppChannelParams = fuYouAppChannelParams(merchantAppChannelDO);
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("version", "1.0");
            params.put("ins_cd", fuYouConfig.getInsCd());
            params.put("mchnt_cd", fuYouAppChannelParams.getFuYouId());
            params.put("term_id", "88888888");
            String payType = payOrderDO.getPayType();
            PayEnums.PAY_TYPE fromByType = PayEnums.PAY_TYPE.fromByType(payType);
            String orderType = payType;
            switch (fromByType) {
                case UNION_APY:
                    orderType = "UNIONPAY";
                    break;
            }
            params.put("order_type", orderType);
            params.put("mchnt_order_no", payOrderDO.getPayOrderNo());
            params.put("random_str", System.currentTimeMillis() + "");
            params.put("sign", SignUtils.getPaySign(params, fuYouConfig));
            log.info("富友订单查询参数:{}", JsonUtils.toJsonString(params));
            String mapToXml = SignUtils.convertMapToXml(params);
            log.info("富友订单查询请求xml参数:{}", mapToXml);
            Map<String, String> requestParams = new HashMap<>();
            mapToXml = URLEncoder.encode(mapToXml, FuYouConstant.charset);
            requestParams.put("req", mapToXml);
            String url = domain(channelPayOrderQueryContext.isTest()) + FuYouConstant.MERCHANT_PAY_ORDER_QUERY;
            log.info("富友订单查询req参数:{}", JsonUtils.toJsonString(requestParams));
            String response = HttpUtils.post(url, requestParams);
            // 响应报文验签
            // FuYouPayOrderResultDto fuYouPayOrderResultDto = SignUtils.xmlToObject(response, FuYouPayOrderResultDto.class);
            Map<String, Object> stringObjectMap = SignUtils.xmlToObject(response);
            FuYouPayOrderResultDto fuYouPayOrderResultDto= BeanUtil.mapToBean(stringObjectMap,FuYouPayOrderResultDto.class,true);
            log.info("富友订单查询返回参数:{}", JsonUtils.toJsonString(fuYouPayOrderResultDto));
            if ("000000".equals(fuYouPayOrderResultDto.getResult_code())) {
                PayOrderDO updartePayOrderDO = new PayOrderDO();
                switch (fuYouPayOrderResultDto.getTrans_stat()) {
                    case "SUCCESS":
                    case "REFUND":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                        updartePayOrderDO.setBankMsg("");
                        break;
                    case "CLOSED":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                        break;
                    case "USERPAYING":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                        break;
                    case "NOTPAY":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                        break;
                    case "REVOKED":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CANCEL.getCode());
                        break;
                }
                if (StringUtils.hasText(fuYouPayOrderResultDto.getReserved_txn_fin_ts())) {
                    try {
                        updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(fuYouPayOrderResultDto.getReserved_txn_fin_ts()));
                    }
                    catch (Exception e) {
                        log.error("富友订单查询返回 时间格式化错误 dateTime:{},message:{}", fuYouPayOrderResultDto.getReserved_txn_fin_ts(), e.getMessage(),e);
                    }
                }
                updartePayOrderDO.setInsOrderNo(fuYouPayOrderResultDto.getReserved_fy_trace_no());
                updartePayOrderDO.setChannelOrderNo(fuYouPayOrderResultDto.getTransaction_id());
                if (fuYouPayOrderResultDto.getReserved_settlement_amt() != null) {
                    updartePayOrderDO.setSettlementAmount(MerchantUtils.divide(new BigDecimal(fuYouPayOrderResultDto.getReserved_settlement_amt()), new BigDecimal(100)));
                }
                if (fuYouPayOrderResultDto.getReserved_coupon_fee() != null) {
                    updartePayOrderDO.setPayAmount(MerchantUtils.subtract(payOrderDO.getAmount(),
                            MerchantUtils.divide(new BigDecimal(fuYouPayOrderResultDto.getReserved_coupon_fee()), new BigDecimal(100))));
                }
                else {
                    updartePayOrderDO.setPayAmount(payOrderDO.getAmount());
                }
                channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
            }
            else {
                String respDesc = fuYouPayOrderResultDto.getResult_msg();
                channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelPayOrderQueryContext.setRemark(respDesc);
                }
                else {
                    channelPayOrderQueryContext.setRemark("富友提交支付订单查询返回错误信息为空,错误码为:" + fuYouPayOrderResultDto.getResult_code());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("支付查询支付订单请求失败 message:{}", e.getMessage(), e);
            channelPayOrderQueryContext.setRemark("斗拱查询支付订单请求失败,失败信息" + e.getMessage());
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    /**
     * 富友关闭订单
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonCloseChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderQueryContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        FuYouConfig fuYouConfig = JsonUtils.parseObject(channelConfig, FuYouConfig.class);
        PayOrderDO payOrderDO = channelPayOrderQueryContext.getPayOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelPayOrderQueryContext.getMerchantAppChannelDO();
        FuYouAppChannelParams fuYouAppChannelParams = fuYouAppChannelParams(merchantAppChannelDO);
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("version", "1.0");
            params.put("ins_cd", fuYouConfig.getInsCd());
            params.put("mchnt_cd", fuYouAppChannelParams.getFuYouId());
            params.put("term_id", "88888888");
            params.put("mchnt_order_no", payOrderDO.getPayOrderNo());
            String payType = payOrderDO.getPayType();
            PayEnums.PAY_TYPE fromByType = PayEnums.PAY_TYPE.fromByType(payType);
            String orderType = payType;
            switch (fromByType) {
                case UNION_APY:
                    orderType = "UNIONPAY";
                    break;
            }
            params.put("order_type", orderType);
            params.put("random_str", System.currentTimeMillis() + "");
            params.put("sub_appid", "");
            params.put("sign", SignUtils.getPaySign(params, fuYouConfig));
            log.info("富友关闭订单参数:{}", JsonUtils.toJsonString(params));
            String mapToXml = SignUtils.convertMapToXml(params);
            log.info("富友订单关闭请求xml参数:{}", mapToXml);
            Map<String, String> requestParams = new HashMap<>();
            mapToXml = URLEncoder.encode(mapToXml, FuYouConstant.charset);
            requestParams.put("req", mapToXml);
            String url = domain(channelPayOrderQueryContext.isTest()) + FuYouConstant.MERCHANT_PAY_ORDER_CLOSE;
            log.info("富友订单关闭req参数:{}", JsonUtils.toJsonString(requestParams));
            String response = HttpUtils.post(url, requestParams);
            // 响应报文验签
            // FuYouPayOrderResultDto fuYouPayOrderResultDto = SignUtils.xmlToObject(response, FuYouPayOrderResultDto.class);
            Map<String, Object> stringObjectMap = SignUtils.xmlToObject(response);
            FuYouPayOrderResultDto fuYouPayOrderResultDto= BeanUtil.mapToBean(stringObjectMap,FuYouPayOrderResultDto.class,true);
            log.info("富友订单关闭返回参数:{}", JsonUtils.toJsonString(fuYouPayOrderResultDto));
            if ("000000".equals(fuYouPayOrderResultDto.getResult_code()) || "030007".equals(fuYouPayOrderResultDto.getResult_code())
                    || "1023".equals(fuYouPayOrderResultDto.getResult_code())) {
                PayOrderDO updartePayOrderDO = new PayOrderDO();
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
            }
            else {
                String respDesc = fuYouPayOrderResultDto.getResult_msg();
                channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelPayOrderQueryContext.setRemark(respDesc);
                }
                else {
                    channelPayOrderQueryContext.setRemark("富友订单关闭返回错误信息为空,错误码为:" + fuYouPayOrderResultDto.getResult_code());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("支付关闭支付订单请求失败 message:{}", e.getMessage(), e);
            channelPayOrderQueryContext.setRemark("斗拱关闭支付订单请求失败,失败信息" + e.getMessage());
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    /**
     * 富友查询关闭订单状态
     * 
     * @param channelPayOrderQueryContext
     */
    public void commonQueryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        // 富友无此接口
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
        channelPayOrderQueryContext.setUpdartePayOrderDO(updartePayOrderDO);
    }

    /**
     * 富友订单退款
     * 
     * @param channelRefundPayOrderContext
     */
    public void commonRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelRefundPayOrderContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        FuYouConfig fuYouConfig = JsonUtils.parseObject(channelConfig, FuYouConfig.class);

        PayOrderDO payOrderDO = channelRefundPayOrderContext.getPayOrderDO();
        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelRefundPayOrderContext.getMerchantAppChannelDO();
        FuYouAppChannelParams fuYouAppChannelParams = fuYouAppChannelParams(merchantAppChannelDO);
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("version", "1.0");
            params.put("ins_cd", fuYouConfig.getInsCd());
            params.put("mchnt_cd", fuYouAppChannelParams.getFuYouId());
            params.put("term_id", "88888888");
            params.put("mchnt_order_no", payOrderDO.getPayOrderNo());
            params.put("random_str", System.currentTimeMillis() + "");
            String payType = payOrderDO.getPayType();
            PayEnums.PAY_TYPE fromByType = PayEnums.PAY_TYPE.fromByType(payType);
            String orderType = payType;
            switch (fromByType) {
                case UNION_APY:
                    orderType = "UNIONPAY";
                    break;
            }
            params.put("order_type", orderType);
            params.put("refund_order_no", refundOrderDO.getRefundOrderNo());
            params.put("total_amt", MerchantUtils.YuanToFen(payOrderDO.getAmount()).toString());
            params.put("refund_amt", MerchantUtils.YuanToFen(refundOrderDO.getRefundAmount()).toString());
            params.put("operator_id", "");

            params.put("sign", SignUtils.getPaySign(params, fuYouConfig));
            log.info("富友提交退款订单参数:{}", JsonUtils.toJsonString(params));
            String mapToXml = SignUtils.convertMapToXml(params);
            log.info("富友提交退款订单请求xml参数:{}", mapToXml);
            Map<String, String> requestParams = new HashMap<>();
            mapToXml = URLEncoder.encode(mapToXml, FuYouConstant.charset);
            requestParams.put("req", mapToXml);
            String url = domain(channelRefundPayOrderContext.isTest()) + FuYouConstant.MERCHANT_PAY_ORDER_REFUND;
            log.info("富友提交退款订单req参数:{}", JsonUtils.toJsonString(requestParams));
            String response = HttpUtils.post(url, requestParams);
            // 响应报文验签
            // FuYouRefundOrderResultDto fuYouPayOrderResultDto = SignUtils.xmlToObject(response, FuYouRefundOrderResultDto.class);
            Map<String, Object> stringObjectMap = SignUtils.xmlToObject(response);
            FuYouPayOrderResultDto fuYouPayOrderResultDto= BeanUtil.mapToBean(stringObjectMap,FuYouPayOrderResultDto.class,true);
            log.info("富友提交退款订单返回参数:{}", JsonUtils.toJsonString(fuYouPayOrderResultDto));
            if ("000000".equals(fuYouPayOrderResultDto.getResult_code())) {
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = fuYouPayOrderResultDto.getResult_msg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("富友提交退款订单返回错误信息为空,错误码为:" + fuYouPayOrderResultDto.getResult_code());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("富友提交退款订单请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("富友提交退款订单请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    /**
     * 富友订单退款状态查询
     * 
     * @param channelRefundPayOrderContext
     */
    public void commonQueryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelRefundPayOrderContext.getProviderChannelConfigDO();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        FuYouConfig fuYouConfig = JsonUtils.parseObject(channelConfig, FuYouConfig.class);

        PayOrderDO payOrderDO = channelRefundPayOrderContext.getPayOrderDO();
        RefundOrderDO refundOrderDO = channelRefundPayOrderContext.getRefundOrderDO();
        MerchantAppChannelDO merchantAppChannelDO = channelRefundPayOrderContext.getMerchantAppChannelDO();
        FuYouAppChannelParams fuYouAppChannelParams = fuYouAppChannelParams(merchantAppChannelDO);
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("version", "1.0");
            params.put("ins_cd", fuYouConfig.getInsCd());
            params.put("mchnt_cd", fuYouAppChannelParams.getFuYouId());
            params.put("term_id", "88888888");
            params.put("refund_order_no", refundOrderDO.getRefundOrderNo());
            params.put("random_str", System.currentTimeMillis() + "");
            params.put("sign", SignUtils.getPaySign(params, fuYouConfig));
            log.info("富友退款订单查询订单参数:{}", JsonUtils.toJsonString(params));
            String mapToXml = SignUtils.convertMapToXml(params);
            log.info("富友退款订单查询请求xml参数:{}", mapToXml);
            Map<String, String> requestParams = new HashMap<>();
            mapToXml = URLEncoder.encode(mapToXml, FuYouConstant.charset);
            requestParams.put("req", mapToXml);
            String url = domain(channelRefundPayOrderContext.isTest()) + FuYouConstant.MERCHANT_PAY_ORDER_REFUND_QUERY;
            log.info("富友退款订单查询req参数:{}", JsonUtils.toJsonString(requestParams));
            String response = HttpUtils.post(url, requestParams);
            // 发起请求
            // FuYouRefundOrderResultDto fuYouPayOrderResultDto = SignUtils.xmlToObject(response, FuYouRefundOrderResultDto.class);
            Map<String, Object> stringObjectMap = SignUtils.xmlToObject(response);
            FuYouPayOrderResultDto fuYouPayOrderResultDto= BeanUtil.mapToBean(stringObjectMap,FuYouPayOrderResultDto.class,true);
            log.info("富友提交退款订单返回参数:{}", JsonUtils.toJsonString(fuYouPayOrderResultDto));
            if ("000000".equals(fuYouPayOrderResultDto.getResult_code())) {
                RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
                switch (fuYouPayOrderResultDto.getTrans_stat()) {
                    case "SUCCESS":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                        break;
                    case "PAYERROR":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
                        break;
                    default:
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
                }
                channelRefundPayOrderContext.setUpdateRefundOrderDO(updateRefundOrderDO);
            }
            else {
                String respDesc = fuYouPayOrderResultDto.getResult_msg();
                channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
                if (StringUtils.hasText(respDesc)) {
                    channelRefundPayOrderContext.setRemark(respDesc);
                }
                else {
                    channelRefundPayOrderContext.setRemark("富友退款订单查询返回错误信息为空,错误码为:" + fuYouPayOrderResultDto.getResult_code());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("富友退款订单查询请求失败 message:{}", e.getMessage(), e);
            channelRefundPayOrderContext.setRemark("富友退款订单查询请求失败,失败信息" + e.getMessage());
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            // channelMerchantAccessQueryContext.setAccessStatus(MerchantEnum.CHANNEL_CONFIG_STATUS.APPROVE_REFUSE);
        }
    }

    public String domain(boolean test) {
        return test ? FuYouConstant.PAY_ORDER_DOMAIN_TEST : FuYouConstant.PAY_ORDER_DOMAIN_PROD;
    }
    public String h5Domain(boolean test) {
        return test ? FuYouConstant.PAY_ORDER_H5_DOMAIN_TEST : FuYouConstant.PAY_ORDER_H5_DOMAIN_PROD;
    }

    public Integer expireMinute(Long time) {
        if (time == null) {
            return 30;
        }
        return (int) (time - System.currentTimeMillis()) / 1000 / 60;
    }

    public String date(Long time) {
        if (time == null) {
            time = System.currentTimeMillis();
        }
        LocalDateTime localDateTime = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDateTime();
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return localDateTime.format(sdf);
    }
    public FuYouAppChannelParams fuYouAppChannelParams(MerchantAppChannelDO merchantAppChannelDO) {
        return JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), FuYouAppChannelParams.class);
    }
}
